package fpc.utils;
import flash.display.Sprite;
import flash.events.Event;
import flash.Lib;
import flash.utils.TypedDictionary;
import haxe.Timer;

class Conveyor
{
	public function new()
	{
		_isWork = false;
		_complete = true;
	}
	
	public function add(f:Void->Dynamic)
	{
		var command = new Task();
		command.f = f;
		addToTail(command);
		_complete = true;
	}
	
	public function addInclude(f:Void->Dynamic)
	{
		add(addBookmark);
		add(f);
		add(removeBookmark);
	}
	
	public function launch()
	{
		if (_complete)
		{
			play();
		}
	}
	
	public function play()
	{
		_complete = false;
		_isWork = true;
		var command = _head;
		while(command != null)
		{
			remove(command);
			command.f();
			if(!_isWork)
			{
				return;
			}
			command = _head;
		}
		_complete = true;
	}
	
	public function stop()
	{
		_isWork = false;
	}
	
	public function clear()
	{
		_head = null;
		_tail = null;
	}
	
	var _complete:Bool;
	
	var _isWork:Bool;
	
	var _bookmarks:Array<Task>;
	
	private function addBookmark()
	{
		if (_bookmarks == null)
		{
			_bookmarks = [];
		}
		var bookmark:Task = new Task();
		addToTail(bookmark);
		_bookmarks.push(bookmark);
	}
	
	private function removeBookmark()
	{
		var bookmark:Task = _bookmarks.pop();
		moveToHeadAfter(bookmark);
		remove(bookmark);
	}
	
	// HELPED
	
	public function addPause(milliseconds:Int)
	{
		add(callback(pause, milliseconds));
	}
	
	var _pauseInterval:Int;
	
	function pause(milliseconds:Int)
	{
		stop();
		Timer.delay(play, milliseconds);
	}
	
	public function addFrames(numFrames:Int)
	{
		add(callback(startEnterFrame, numFrames));
	}
	
	static var _sprite:Sprite;
	
	var _numFrames:Int;
	
	function startEnterFrame(numFrames:Int)
	{
		stop();
		if (_sprite == null)
		{
			_sprite = new Sprite();
		}
		_numFrames = numFrames;
		_sprite.addEventListener(Event.ENTER_FRAME, onEnterFrame);
	}
	
	function onEnterFrame(e:Event)
	{
		if (--_numFrames <= 0)
		{
			_sprite.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			play();
		}
	}
	
	// LIST
	
	var _head:Task;
	
	var _tail:Task;
	
	inline function addToTail(command:Task)
	{
		if (_tail == null)
		{
			_tail = _head = command;
			command.previous = null;
		}
		else
		{
			_tail.next = command;
			command.previous = _tail;
			_tail = command;
		}
		command.next = null;
	}
	
	inline function addToHead(command:Task)
	{
		if (_head == null)
		{
			_head = _tail = command;
			command.next = null;
		}
		else
		{
			_head.previous = command;
			command.next = _head;
			_head = command;
		}
		command.previous = null;
	}
	
	inline function remove(command:Task)
	{
		var previous = command.previous;
		var next = command.next;
		command.previous = null;
		command.next = null;
		if (previous != null)
		{
			previous.next = next;
		}
		if (next != null)
		{
			next.previous = previous;
		}
		if (command == _head)
		{
			_head = next;
		}
		if (command == _tail)
		{
			_tail = previous;
		}
	}
	
	inline function addAfter(previous:Task, command:Task)
	{
		var next = previous.next;
		previous.next = command;
		command.previous = previous;
		command.next = next;
		next.previous = command;
		if (previous == _tail)
		{
			if (previous == _head)
			{
				_head = command;
			}
			_tail = command;
		}
	}
	
	inline function moveToHeadAfter(command:Task)
	{
		var lastHead = _head;
		var lastTail = _tail;
		var nextHead = command.next;
		if (nextHead != null)
		{
			_head = nextHead;
			_head.previous = null;
			_tail = command;
			_tail.next = null;
			lastHead.previous = lastTail;
			lastTail.next = lastHead;
		}
	}
}

private class Task
{
	public function new()
	{
		
	}
	
	public var f:Void->Dynamic;
	
	public var previous:Task;
	
	public var next:Task;
}